﻿#-------------------------------------------------------------------------------
# Name:        dataParser.py
# Purpose:
#
# Author:      yoyo
#
# Created:     20-02-2008
# Copyright:   (c) yoyo 2008
# Licence:     <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python

#import cStringIO as StringIO
import StringIO

import struct
import md5

#数据类型
TYPE_NONE = 0
TYPE_CS_LOGIN = 1               #登陆
TYPE_CS_REQUEST_URL = 2         #请求URL资源
TYPE_CS_RESPONSE_URLLIST = 3    #查询真实url的返回数据

TYPE_SC_RESPONSE_LOGIN = 11     #回应登陆
TYPE_SC_URLPIECES = 12          #返回URL可下载块列表
TYPE_SC_SAVE_URLPIECES = 13     #主动要求客户下载保存某些块
TYPE_SC_REQUEST_URLLIST = 14    #通过urlmd5值，查询真实的url

TYPE_CC_REQUEST_URLPIECES = 21  #客户端发送请求块信息
TYPE_CC_RESPONSE_PIECE = 22     #客户端返回块内容

#数据格式,struct模块用到的格式
FMT_INT = 'i'
FMT_urlCost = 'i'
FMT_Count = 'i'
FMT_Urlmd5 = '32s'
FMT_Pieces = '256s'
FMT_PeerID = '20s'

LEN_INT = 4
LEN_Urlmd5 = 32
LEN_Pieces = 256
LEN_PeerID = 20



class DataParser(object):
    """数据分析"""
    
    def __init__(self, store):
        """ 初始化 """
        self.store = store
        
    def parserdata(self, data):
        """ 解释接受到的数据，返回内部使用的数据对象 """
        ifdata = StringIO.StringIO(data)
        try:
            iver =  int(ifdata.read(1))
            itype = int(ifdata.read(1))
            if iver == 1:
                return (iver, itype, ParserVER1._parserfunc(itype)(ifdata))
        except:
            pass
            
    


class Parser:
    def __init__(self):
        pass
    
    @staticmethod
    def parser_none(fdata):
        pass
    
    @classmethod
    def _parserfunc(cls, datatype):
        if datatype == TYPE_CS_LOGIN:
            return cls.parser_cs_login
        elif datatype == TYPE_CS_REQUEST_URL:
            return cls.parser_cs_request_url
        else:
            return None #return cls.parser_none

class ParserVER1(parser):
    def __init__(self):
        self.version = 1
    
    @staticmethod
    def parser_count(fdata, fmt):
        """ 解释Count代表的重复数据,数据格式为fmt """
        icount = struct.unpack(FMT_INT, fdata.read(LEN_INT))
        ifmt = fmt.join([i and '' for i in range(1, icount+2)])
        #返回tuple
        return (icount, struct.unpack(ifmt, fdata.read()))
            
    
    @staticmethod
    def parser_CS_LOGIN(fdata):
        """ 登陆后，发送客户保存的块列表:FileSize(int)+Count+(Urlmd5+Pieces)列表 """
        ifilesize = struct.unpack(FMT_INT, fdata.read(LEN_INT))
        return (ifilesize, parser_count(fdata, FMT_Urlmd5+FMT_Pieces))
    #parser_TYPE_CS_LOGIN = staticmethod(parser_TYPE_CS_LOGIN)

    @staticmethod
    def parser_CS_REQUEST_URL(fdata):
        """ 请求某url可用的下载块列表:Urlmd5+Pieces """
        return struct.unpack(FMT_Urlmd5 + FMT_Pieces, fdata.read())
    
    @staticmethod
    def parser_CS_RESPONSE_URLLIST(fdata):
        """ 查询真实url的返回数据:(urlmd5+url+回车符(\n))列表 """
        ilist = []
        for str in fdata.readlines():
            ilist.append((str[0:LEN_Urlmd5], str[LEN_Urlmd5:-1]))
        return ilist

    @staticmethod
    def parser_SC_RESPONSE_LOGIN(fdata):
        """ 发送客户端登陆回应信息:Count+(urlmd5+urlCost)列表 """
        return parser_count(fdata, FMT_Urlmd5 + FMT_urlCost)
    
    @staticmethod
    def parser_SC_URLPIECES(fdata):
        """ 发送某url可用的下载块列表:Urlmd5+UrlCost+Count+PeerID列表 """
        iurlmd5, iurlcost = struct.unpack(FMT_Urlmd5 + FMT_urlCost, \
            fdata.read(LEN_Urlmd5+LEN_INT))
        return (iurlmd5, iurlcost, parser_count(fdata, FMT_PeerID))

    @staticmethod
    def parser_SC_SAVE_URLPIECES(fdata):
        """ 主动要求客户下载保存某些块:UrlCost+Pieces+Url """
        iurlcost = struct.unpack(FMT_urlCost, fdata.read(LEN_INT))
        return (iurlcost, fdata.read(LEN_Pieces), fdata.read())
    
    @staticmethod
    def parser_SC_REQUEST_URLLIST(fdata):
        """ 通过urlmd5值，查询真实的url:Count+Urlmd5列表 """
        return parser_count(fdata, FMT_Urlmd5)
    
    @staticmethod
    def parser_CC_REQUEST_URLPIECES(fdata):
        """ 客户端发送请求块信息:urlmd5+UrlCost+Pieces """
        return struct.unpack(FMT_Urlmd5 + FMT_urlCost + FMT_Pieces, fdata.read())
        
    @staticmethod
    def parser_CC_RESPONSE_PIECE(fdata):
        """ 客户端返回块内容:urlmd5+PieceID+Data """
        iurlmd5, ipieceid = struct.unpack(FMT_Urlmd5 + FMT_PeerID, \
            fdata.read(LEN_Urlmd5 + LEN_PeerID))
        return (iurlmd5, ipieceid, fdata.read())
    


class Pack:
    """ 包装数据 """
    def __init__(self, store):
        self.store = store
        

class PackVER1:
    def __init__(self, store):
        self.version = 1
        Pack.__init__(self, store)

    def pack_count(self, count, fmt, argslist):
        """ 组装count类型的数据 """
        ifmt = fmt.join([i and '' for i in range(1, count+2)])
        return struct.pack(FMT_INT + ifmt, count, *argslist)
        
    def pack_CS_LOGIN(self, filesize, count, urlmd5_pieces_list):
        """ 登陆后，发送客户保存的块列表:FileSize(int)+Count+(Urlmd5+Pieces)列表 """
        return struct.pack(FMT_INT, filesize) + \
            self.pack_count(count, FMT_Urlmd5 + FMT_Pieces, urlmd5_pieces_list)

    def pack_CS_REQUEST_URL(self, urlmd5, pieces):
        """ 请求某url可用的下载块列表:Urlmd5+Pieces """
        #return struct.pack(FMT_Urlmd5 + FMT_Pieces, urlmd5, pieces)
        return urlmd5 + pieces
    
    def pack_CS_RESPONSE_URLLIST(self, urlmd5_url_list):
        """ 查询真实url的返回数据:(urlmd5+url+回车符(\n))列表 """
        sio1 = StringIO.StringIO()
        for i in range(len(urlmd5_url_list)):
            sio1.write(urlmd5_url_list[i])
            if i%2==1:
                sio1.write('\n')
                
        sio1.seek(0)
        return sio1.read()
            
    def pack_SC_RESPONSE_LOGIN(self, count, urlmd5_urlcost_list):
        """ 发送客户端登陆回应信息:Count+(urlmd5+urlCost)列表 """
        return self.pack_count(count, FMT_Urlmd5 + FMT_urlCost, urlmd5_urlcost_list)
    
    def pack_SC_URLPIECES(self, urlmd5, urlcost, count, peerid_list):
        """ 发送某url可用的下载块列表:Urlmd5+UrlCost+Count+PeerID列表 """
        return ''.join((urlmd5, struct.pack(FMT_urlCost + FMT_Count, urlcost, count)\
            , ''.join(peerid_list)))

    def pack_SC_SAVE_URLPIECES(self, urlcost, pieces, url):
        """ 主动要求客户下载保存某些块:UrlCost+Pieces+Url """
        return ''.join((struct.pack(FMT_urlCost, urlcost), pieces, url))
    
    
    def pack_SC_REQUEST_URLLIST(self, count, urlmd5_list):
        """ 通过urlmd5值，查询真实的url:Count+Urlmd5列表 """
        return self.pack_count(count, FMT_Urlmd5, urlmd5_list)
    
    def parser_CC_REQUEST_URLPIECES(self, urlmd5, urlcost, pieces):
        """ 客户端发送请求块信息:urlmd5+UrlCost+Pieces """
        return ''.join((urlmd5, struct.pack(FMT_urlCost, urlcost), pieces))
    
    
    def parser_CC_RESPONSE_PIECE(self, urlmd5, pieceid, data):
        """ 客户端返回块内容:urlmd5+PieceID+Data """
        return ''.join((urlmd5, struct.pack(FMT_INT, pieceid), data))
    





################################
def test():
    assert parserVER1._parserfunc(TYPE_CS_LOGIN) == parserVER1.parser_TYPE_CS_LOGIN,\
         '类方法错误'
    
if __name__ == '__main__':
    test()
    


    
